home *** CD-ROM | disk | FTP | other *** search
- /* See the file Distribution for distribution terms.
- (c) Copyright 1994 Ari Halberstadt */
-
- /* A simple program to test my Popup CDEF. A dialog containing various
- types of popup controls is displayed. You can make selections from the
- menus, and notice how the current item is marked with a check mark.
- Click the "Quit" button to quit the program.
-
- A type-in popup menu is also demonstrated, and a functions that show
- how type-in popup menus can be supported are provided. To make it
- easier to find the relavent code, sections of code that are used
- for the type-in popup menu are bracketed with "TYPEIN_BEGIN" and
- "TYPEIN_END".
-
- 94/08/25 aih - custom item is set with SetMenuItemText so metacharacters
- aren't interpreted
- 94/07/20 aih - shows both my CDEF and the system 7.0 CDEF
- 94/07/07 aih - adapted for universal headers
- 94/03/14 aih - created by hacking Thread Library test application */
-
- #include <stdio.h>
- #include <strings.h>
- #include <Desk.h>
- #include <Dialogs.h>
- #include <Events.h>
- #include <Fonts.h>
- #include <GestaltEqu.h>
- #include <LowMem.h>
- #include <Memory.h>
- #include <Menus.h>
- #include <QuickDraw.h>
- #include <OSEvents.h>
- #include <OSUtils.h>
- #include <Resources.h>
- #include <TextUtils.h>
- #include <ToolUtils.h>
- #include <Traps.h>
- #include <Windows.h>
- #include "PopupLib.h"
-
- /*----------------------------------------------------------------------------*/
- /* global definitions and declarations */
- /*----------------------------------------------------------------------------*/
-
- #define CDEF_ATTACH (1)
-
- /* It is much easier to debug an application than it is to debug
- a code resource. So that the CDEF can be debugged from within
- an application, we can attach the CDEF compiled as part of this
- application to a popup menu control, replacing the 'CDEF'
- resource for the control. If you define CDEF_ATTACH as 1, then
- the CDEF will be "attached", so that it can be debugged within
- this application. */
- #ifndef CDEF_ATTACH
- #define CDEF_ATTACH (0)
- #endif /* CDEF_ATTACH */
-
- /* Number of dialogs we create. */
- #define MAXDLG (3)
-
- /* structure of a 'CNTL' resource */
- typedef struct {
- Rect bounds;
- short value;
- Boolean visible;
- Boolean fill;
- short max;
- short min;
- short procID;
- long refCon;
- Str255 title;
- } ControlTemplate, *ControlTemplatePtr, **ControlTemplateHandle;
-
- /* dialog items */
- enum {
- rDialog = 128,
- iQuit = 1,
- iFontPopup,
- iSizeTitle,
- iSizeText,
- iSizePopup,
- iStylePopup,
- iAlignPopup,
- iLongTitlePopup,
- iIconsPopup,
- iRightAlignedPopup,
- iWindowFontPopup,
- iDisabledItemPopup,
- iDisabledPopup,
- iNoTitlePopup,
- iColorPopup,
- iToggleDisabledPopup,
- iPromptText,
- iLast
- };
-
- /*----------------------------------------------------------------------------*/
- /* assertions */
- /*----------------------------------------------------------------------------*/
-
- #ifndef NDEBUG
- #define myassert(x) ((void) ((x) || assertfailed()))
- #else
- #define myassert(x) ((void) 0)
- #endif
-
- #define require(x) myassert(x)
- #define check(x) myassert(x)
- #define ensure(x) myassert(x)
-
- static int assertfailed(void)
- {
- DebugStr((StringPtr) "\p An assertion failed.");
- return(0);
- }
-
- /*----------------------------------------------------------------------------*/
- /* standard Macintosh initializations */
- /*----------------------------------------------------------------------------*/
-
- /* initialize application heap */
- static void HeapInit(long stack, short masters)
- {
- SetApplLimit(LMGetApplLimit() - stack);
- MaxApplZone();
- while (masters-- > 0)
- MoreMasters();
- }
-
- /* initialize managers */
- static void ManagersInit(void)
- {
- EventRecord event;
- short i;
-
- /* standard initializations */
- InitGraf((Ptr) &qd.thePort);
- InitFonts();
- InitWindows();
- InitMenus();
- TEInit();
- InitDialogs(0);
- FlushEvents(everyEvent, 0);
- InitCursor();
-
- /* so first window will be frontmost */
- for (i = 0; i < 4; i++)
- EventAvail(everyEvent, &event);
- }
-
- /*----------------------------------------------------------------------------*/
- /* gestalt utilities */
- /*----------------------------------------------------------------------------*/
-
- /* return the version of the Macintosh system software */
- static short MacVersion(void)
- {
- OSErr err;
- long response;
-
- err = Gestalt(gestaltSystemVersion, &response);
- return(! err ? LoWord(response) : 0);
- }
-
- /*----------------------------------------------------------------------------*/
- /* event utilities */
- /*----------------------------------------------------------------------------*/
-
- /* Functions for determining whether a trap is available. Based on
- functions given in IM VI. */
-
- /* return number of toolbox traps */
- static short TrapNumToolbox(void)
- {
- short result = 0;
-
- if (NGetTrapAddress(_InitGraf, ToolTrap) == NGetTrapAddress(0xAA6E, ToolTrap))
- result = 0x0200;
- else
- result = 0x0400;
- return(result);
- }
-
- /* return the type of the trap */
- static TrapType TrapTypeGet(short trap)
- {
- return((trap & 0x0800) > 0 ? ToolTrap : OSTrap);
- }
-
- /* true if the trap is available */
- static Boolean TrapAvailable(short trap)
- {
- TrapType type;
-
- type = TrapTypeGet(trap);
- if (type == ToolTrap) {
- trap &= 0x07FF;
- if (trap >= TrapNumToolbox())
- trap = _Unimplemented;
- }
- return(NGetTrapAddress(trap, type) != NGetTrapAddress(_Unimplemented, ToolTrap));
- }
-
- /* true if the WaitNextEvent trap is available */
- static Boolean MacHasWNE(void)
- {
- static Boolean initialized;
- static Boolean wne;
-
- if (! initialized) {
- /* do only once for efficiency */
- wne = TrapAvailable(_WaitNextEvent);
- initialized = true;
- }
- return(wne);
- }
-
- /* Call GetNextEvent or WaitNextEvent, depending on which one is available.
- The parameters to this function are identical to those to WaitNextEvent.
- If GetNextEvent is called the extra parameters are ignored. */
- static Boolean EventGet(short mask, EventRecord *event,
- unsigned long sleep, RgnHandle cursor)
- {
- Boolean result = false;
-
- if (MacHasWNE())
- result = WaitNextEvent(mask, event, sleep, cursor);
- else {
- SystemTask();
- result = GetNextEvent(mask, event);
- }
- if (! result) {
- /* make sure it's a null event, even if the system thinks otherwise, e.g.,
- some desk accessory events (see comment in TransSkell event loop) */
- event->what = nullEvent;
- }
- return(result);
- }
-
- /*----------------------------------------------------------------------------*/
- /* dialog utilities */
- /*----------------------------------------------------------------------------*/
-
- /* get the text of the dialog item */
- static void GetDialogText(DialogPtr dlg, short item, Str255 str)
- {
- short type;
- Handle hitem;
- Rect box;
-
- GetDialogItem(dlg, item, &type, &hitem, &box);
- GetDialogItemText(hitem, str);
- }
-
- /* set the text of the dialog item */
- static void SetDialogText(DialogPtr dlg, short item, ConstStr255Param str)
- {
- short type;
- Handle hitem;
- Rect box;
-
- GetDialogItem(dlg, item, &type, &hitem, &box);
- SetDialogItemText(hitem, str);
- }
-
- /* return the numeric value of the dialog item */
- static long GetDialogNumber(DialogPtr dlg, short item)
- {
- long num;
- Str255 str;
-
- GetDialogText(dlg, item, str);
- StringToNum(str, &num);
- return(num);
- }
-
- /* set the text of the dialog item to the number */
- static void SetDialogNumber(DialogPtr dlg, short item, long num)
- {
- Str255 str;
-
- NumToString(num, str);
- SetDialogText(dlg, item, str);
- }
-
- /* return the control handle for the item */
- static ControlHandle GetDialogControl(DialogPtr dlg, short item)
- {
- short type;
- Handle hitem;
- Rect box;
-
- GetDialogItem(dlg, item, &type, &hitem, &box);
- return((ControlHandle) hitem);
- }
-
- /* return a handle to the popup control's menu */
- static MenuHandle GetControlMenu(ControlHandle ctl)
- {
- return((**(PopupPrivateHandle) (**ctl).contrlData).mHandle);
- }
-
- /*----------------------------------------------------------------------------*/
- /* menu utilities */
- /*----------------------------------------------------------------------------*/
-
- /* Given a font family id and true in 'outlined', OutlineFontSizes will
- outline all items in a size menu that actually exist in that font. If
- 'outlined' is false, all items will be set to plain text, which is
- useful if you don't have any font information. */
- static void OutlineFontSizes(MenuHandle menu, short family, Boolean outlined)
- {
- short nitems; /* number of items in menu */
- short item; /* current item number */
- long size; /* size of current menu item */
- Str255 name; /* name of item */
- Boolean found; /* flag that we've already found the menu item */
-
- found = false;
- nitems = CountMItems(menu);
- for (item = 1; item <= nitems; item++) {
- GetMenuItemText(menu, item, name);
- StringToNum(name, &size);
- if (outlined && RealFont(family, size))
- SetItemStyle(menu, item, outline);
- else
- SetItemStyle(menu, item, 0);
- }
- }
-
- /* TYPEIN_BEGIN */
- /* return item number with given title, or 0 if not found */
- static short FindMenuItem(MenuHandle menu, ConstStr255Param ptitle)
- {
- short item; /* index to menu items */
- short nitems; /* number of items in menu */
- Str255 name; /* name of current item */
-
- nitems = CountMItems(menu);
- for (item = 1; item <= nitems; item++) {
- GetMenuItemText(menu, item, name);
- if (EqualString(ptitle, name, false, true))
- break;
- }
- return(item <= nitems ? item : 0);
- }
- /* TYPEIN_END */
-
- /* TYPEIN_BEGIN */
- /* AdjustTypeInPopupMenu adjusts a type-in popup menu. The 'dlg' parameter
- is a pointer to the dialog containing the type-in popup meun. The
- 'popupItem' parameter is the item number of a type-in popup control. The
- 'textItem' parameter is the item number of the type-in editable text field.
- The 'insertedCustomValue' parameter should initially be false; subsequently,
- it must contain the value returned by the previous call to
- AdjustTypeInPopupMenu.
-
- You should call AdjustTypeInPopupMenu whenever there's a click in the
- popup menu control, but before TrackControl (or DialogSelect) is called
- to handle the click. If the user entered a value into the text field that
- is not in the popup menu, then the user's entry is inserted as the
- first item in the popup menu and a dashed line is inserted to separate
- the user's entry from the predefined values in the menu. When the user
- chooses a menu item or enters a value into the text field that is one of
- the predefined values in the menu, then the items inserted into the menu
- are deleted. The 'insertedCustomValue' flag is used to determine if a
- user's entry was inserted into the menu. */
- static Boolean AdjustTypeInPopupMenu(DialogPtr dlg,
- short popupItem, short textItem,
- Boolean insertedCustomValue)
- {
- ControlHandle ctl; /* handle to size popup control */
- MenuHandle menu; /* handle to popup menu's handle */
- short item; /* index to item in menu */
- short nitems; /* number of items in size menu */
- Str255 text; /* string in text item */
-
- /* adjust user's choice */
- GetDialogText(dlg, textItem, text);
- ctl = GetDialogControl(dlg, popupItem);
- menu = GetControlMenu(ctl);
- item = FindMenuItem(menu, text);
- if (item == 0 && *text) {
-
- /* user entered a size not found in the menu, so add the user's
- choice as the first item in the menu (IM-VI, p2-37) */
- if (! insertedCustomValue) {
- InsertMenuItem(menu, (StringPtr) "\p(-", 0);
- InsertMenuItem(menu, (StringPtr) "\pnot empty", 0);
- SetMenuItemText(menu, 1, text);
- for (nitems = CountMItems(menu); nitems > 0; nitems--)
- SetItemMark(menu, nitems, noMark);
- SetItemMark(menu, 1, checkMark);
- SetControlMaximum(ctl, GetControlMaximum(ctl) + 2);
- insertedCustomValue = true;
- }
- SetMenuItemText(menu, 1, text);
- SetControlValue(ctl, 1);
- }
- else if ((item > 2 || *text == 0) && insertedCustomValue) {
-
- /* remove user's choice, since selected item is in menu
- (or selected item is empty) */
- insertedCustomValue = false;
- SetControlValue(ctl, 1);
- DeleteMenuItem(menu, 1);
- DeleteMenuItem(menu, 1);
- SetControlMaximum(ctl, GetControlMaximum(ctl) - 2);
- SetControlValue(ctl, item - 2);
- }
- else
- SetControlValue(ctl, item);
-
- return(insertedCustomValue);
- }
- /* TYPEIN_END */
-
- /*----------------------------------------------------------------------------*/
- /* the event loop */
- /*----------------------------------------------------------------------------*/
-
- /* create the dialog and run the program */
- static void EventLoop(DialogPtr *dialogs)
- {
- Boolean sizeHasCustomValue;/* true if inserted a value into type-in menu */
- EventRecord event; /* event record for getting next event */
- Boolean quit; /* true if time to quit application */
- Rect dragRect; /* rectangle within which to drag windows */
- Str255 str; /* utility string */
- DialogPtr dlgHit; /* for handling dialog events */
- short itemHit; /* for handling dialog events */
- Point where; /* for handling dialog events */
- short font; /* for getting font */
- short i; /* index to dialogs */
-
- sizeHasCustomValue = false;
- quit = false;
- while (! quit) {
-
- /* Set the dialog's font and font size so we can see the effect of the
- useWFont variation code. We need to do this once every time through
- the event loop since (in system 6.0) DialogSelect resets the font
- to the system font. */
- for (i = 0; dialogs[i]; i++) {
- SetPort(dialogs[i]);
- TextFont(geneva);
- TextSize(9);
- }
-
- /* handle the next event; this is a pretty simple event loop */
- SetCursor(&qd.arrow);
- (void) EventGet(everyEvent, &event, GetCaretTime(), NULL);
- switch (event.what) {
- case mouseDown:
- switch (FindWindow(event.where, &dlgHit)) {
- case inDrag:
- /* handle a click in the drag bar */
- SelectWindow(dlgHit);
- dragRect = (**GetGrayRgn()).rgnBBox;
- DragWindow(dlgHit, event.where, &dragRect);
- break;
- case inContent:
- SelectWindow(dlgHit);
- where = event.where;
- SetPort(dlgHit);
- GlobalToLocal(&where);
- /* TYPEIN_BEGIN */
- /* check if user is clicking in size type-in popup menu */
- for (i = 0; dialogs[i]; i++) {
- if (dlgHit == dialogs[i]) {
- if (FindDialogItem(dlgHit, where) + 1 == iSizePopup) {
-
- /* adjust the size popup menu before it's pulled down */
- sizeHasCustomValue = AdjustTypeInPopupMenu(dlgHit,
- iSizePopup, iSizeText,
- sizeHasCustomValue);
-
- /* outline font sizes available in the selected font */
- GetFNum(str, &font);
- GetMenuItemText(GetControlMenu(GetDialogControl(dlgHit, iFontPopup)),
- GetControlValue(GetDialogControl(dlgHit, iFontPopup)), str);
- OutlineFontSizes(GetControlMenu(GetDialogControl(dlgHit, iSizePopup)),
- font, true);
- }
- break;
- }
- }
- /* TYPEIN_END */
- break;
- }
- break;
- }
-
- /* handle dialog events */
- if (IsDialogEvent(&event) && DialogSelect(&event, &dlgHit, &itemHit)) {
-
- /* handle a click in one of the dialog's buttons */
- switch (itemHit) {
- case iSizePopup:
- /* TYPEIN_BEGIN */
- /* Set the value displayed in the text field to the value
- chosen from the size popup menu and select the text in
- the size edit field. */
- GetMenuItemText(GetControlMenu(GetDialogControl(dlgHit, iSizePopup)),
- GetControlValue(GetDialogControl(dlgHit, iSizePopup)), str);
- SetDialogText(dlgHit, iSizeText, str);
- SelectDialogItemText(dlgHit, iSizeText, 0, 32767);
- /* TYPEIN_END */
- break;
- case iToggleDisabledPopup:
- SetControlValue(GetDialogControl(dlgHit, iToggleDisabledPopup),
- ! GetControlValue(GetDialogControl(dlgHit, iToggleDisabledPopup)));
- if (GetControlValue(GetDialogControl(dlgHit, iToggleDisabledPopup))) {
- SetControlTitle(GetDialogControl(dlgHit, iDisabledPopup),
- (StringPtr) "\pEnabled:");
- HiliteControl(GetDialogControl(dlgHit, iDisabledPopup), 1);
- }
- else {
- SetControlTitle(GetDialogControl(dlgHit, iDisabledPopup),
- (StringPtr) "\pDisabled:");
- HiliteControl(GetDialogControl(dlgHit, iDisabledPopup), 255);
- }
- break;
- case iQuit:
- quit = true;
- break;
- }
- }
-
- /* TYPEIN_BEGIN */
- /* Remove the custom value from the menu, so we don't interfere
- with the size popup menu in the other dialog (where the other
- dialog is the dialog that uses either my popup CDEF or the
- system popup CDEF). We only do this because we're sharing
- the same menu handle with two control items, and because the
- menu handle must contain different data in each control. */
- if (sizeHasCustomValue) {
- ControlHandle ctl = GetDialogControl(dlgHit, iSizePopup);
- MenuHandle menu = GetControlMenu(ctl);
- sizeHasCustomValue = false;
- SetControlValue(ctl, 1);
- DeleteMenuItem(menu, 1);
- DeleteMenuItem(menu, 1);
- SetControlMaximum(ctl, GetControlMaximum(ctl) - 2);
- SetControlValue(ctl, 1);
- }
- /* TYPEIN_END */
- }
- }
-
- /*----------------------------------------------------------------------------*/
- /* initialization */
- /*----------------------------------------------------------------------------*/
-
- /* abort on error */
- static void fatal(Boolean exit, ConstStr255Param msg)
- {
- if (exit) {
- DebugStr(msg);
- ExitToShell();
- }
- }
-
- /* create a dialog using the specified procID */
- static DialogPtr CreateDialogUsingProcID(short procID, short slot,
- const char *prompt, ConstStr255Param title)
- {
- DialogPtr dlg; /* the dialog */
- Point position; /* position of dialog */
- Str255 promptFormat; /* for formatting prompt string */
- char promptText[256]; /* for setting prompt string */
- short i; /* index to 'CNTL' resources */
- short ncntl; /* number of 'CNTL' resources */
- ControlTemplateHandle cntl;/* handle to 'CNTL' resource */
-
- /* change the 'CNTL' resources to use the specified procID */
- ncntl = Count1Resources('CNTL');
- for (i = 1; i <= ncntl; i++) {
- cntl = (ControlTemplateHandle) Get1IndResource('CNTL', i);
- if (cntl && *cntl) {
- HNoPurge((Handle) cntl);
- if (((**cntl).procID & 0xFFF0) != procID)
- (**cntl).procID = ((**cntl).procID & 0x000F) + procID;
- }
- }
-
- /* create the dialog */
- dlg = GetNewDialog(rDialog, NULL, (WindowPtr) -1);
- fatal(! dlg, "\p nil dialog pointer");
-
- /* release all the 'CNTL' resources (so they'll be reloaded from
- disk the next time we're called) */
- for (i = 1; i <= ncntl; i++) {
- cntl = (ControlTemplateHandle) Get1IndResource('CNTL', i);
- if (cntl && *cntl)
- ReleaseResource((Handle) cntl);
- }
-
- /* select the type-in menu's text field */
- SelectDialogItemText(dlg, iSizeText, 0, 32767);
-
- /* disable a popup menu control so we can see what a disabled popup
- menu control looks like */
- HiliteControl(GetDialogControl(dlg, iDisabledPopup), 255);
-
- /* set prompt string */
- GetDialogText(dlg, iPromptText, promptFormat);
- sprintf(promptText, p2cstr(promptFormat), prompt);
- SetDialogText(dlg, iPromptText, c2pstr(promptText));
- SetWTitle(dlg, title);
-
- /* position dialog */
- position.h = qd.screenBits.bounds.left + GetMBarHeight() + 4 + 10 * slot;
- position.v = qd.screenBits.bounds.top + GetMBarHeight() + 4 + 20 * (slot + 1);
- MoveWindow(dlg, position.h, position.v, false);
-
- return(dlg);
- }
-
- /* create the dialog using the system 7 CDEF */
- static DialogPtr CreateDialogUsingSystemCDEF(void)
- {
- DialogPtr dlg;
-
- dlg = CreateDialogUsingProcID(popupMenuProc, 0,
- "the system", (StringPtr) "\pSystem Popup CDEF");
- return(dlg);
- }
-
- /* create the dialog using my popup CDEF */
- static DialogPtr CreateDialogUsingMyCDEF(void)
- {
- DialogPtr dlg; /* the dialog */
- short i; /* index to popup menu items */
- const short popups[] = { /* all of the popup menu items in the dialog */
- iFontPopup,
- iSizePopup,
- iStylePopup,
- iAlignPopup,
- iLongTitlePopup,
- iIconsPopup,
- iRightAlignedPopup,
- iWindowFontPopup,
- iDisabledItemPopup,
- iDisabledPopup,
- iNoTitlePopup,
- iColorPopup,
- 0,
- };
-
- dlg = CreateDialogUsingProcID(kPopupProcID, 1, "my", (StringPtr) "\pMy Popup CDEF");
-
- #if CDEF_ATTACH
- /* It is much easier to debug an application than it is to debug
- a code resource. So that the CDEF can be debugged from within
- an application, we attach the CDEF compiled as part of this
- application to a popup menu control, replacing the 'CDEF'
- resource for the control. */
- for (i = 0; popups[i]; i++)
- PopupCDEFAttach(GetDialogControl(dlg, popups[i]));
- #endif /* CDEF_ATTACH */
-
- return(dlg);
- }
-
- /* create the dialogs and run the event loop */
- static void Run(void)
- {
- DialogPtr dialogs[MAXDLG]; /* array of dialogs we created */
- DialogPtr dlgSysCDEF; /* dialog using system popup CDEF */
- DialogPtr dlgMyCDEF; /* dialog using my popup CDEF */
- short ndialogs; /* number of dialogs we created */
- short i; /* index to array of dialogs */
-
- /* initialize dialogs */
- ndialogs = 0;
- dlgSysCDEF = dlgMyCDEF = NULL;
- for (i = 0; i < MAXDLG; i++)
- dialogs[i] = NULL;
-
- /* create a dialog using the system popup CDEF */
- if (MacVersion() >= 0x0700)
- dlgSysCDEF = dialogs[ndialogs++] = CreateDialogUsingSystemCDEF();
-
- /* create a dialog using my popup CDEF */
- dlgMyCDEF = dialogs[ndialogs++] = CreateDialogUsingMyCDEF();
-
- /* show the dialogs */
- SelectWindow(dlgMyCDEF);
- ShowWindow(dlgMyCDEF);
- if (dlgSysCDEF)
- ShowWindow(dlgSysCDEF);
-
- /* run the event loop */
- EventLoop(dialogs);
-
- /* we could detach the popup menus (with PopupCDEFDetach) and dispose of the
- dialogs, but since we're quitting the application there's no need to */
- }
-
- void main(void)
- {
- HeapInit(0, 4);
- ManagersInit();
- Run();
- }
-